本博客所有案例的开发环境为 VS2013 + SQL Server 2012

Entity Framework实体框架简介

Entity Framework 实体框架(EF)是ADO.NET的开源对象关系映射(ORM)框架,它是 .NET Framework 的一部分,是一组支持开发面向数据的软件应用程序的技术。微软为实现 .NET 跨平台,将 EF 从 .NET 分离,使EF更加组件化。2016年6月27日,Entity Framework 7作为实体框架核心 1.0 发布,与 ASP.Net Core 1.0 和 .Net Core 1.0 一起发布,重命名为凸显出它是完全重新而不是增量升级,同时,EF7并不会取代EF6。

在EF中的实体数据模型(EDM)由以下三种模型和具有相应文件扩展名的映射文件进行定义。
概念架构定义语言文件(.csdl)——定义概念模型
存储架构定义语言文件(.ssdl)——定义存储模型
映射规范语言文件(.msl)——定义存储模型与概念模型之间的映射M。

实体框架 使用这些基于XML的模型和映射文件将对概念模型中的实体和关系的创建、读取、更新、和删除操作转换为数据源中的等效操作。EDM 甚至支持将概念模型中的实体映射到数据源中的存储过程。

ORM(Object Relational Mapping)框架采用元数据来描述对象一关系映射细节,元数据一般采用XML格式,并且存放在专门的对象一映射文件中。

广义上来说,ORM 可以被认为是 面向对象模型和关系型数据库的数据结构之间的相互转换。

狭义上来说,ORM 可以被认为是,基于关系型数据库的数据存储,实现了一个虚拟的面向对象的数据访问接口。理想情况下,基于这样一个面对对象的接口,持续化一个OO对象应该不需要了解任何关系型数据库存储数据的实现细节。

其他.NET ORM框架:

  1. PetaPoco : 与完备的 ORM 框架不同,PetaPoco 更加注重易用性和性能。使用 PataPoco 只需要引入一个C#文件,可以使用强类型的 POCO(Plain Old CLR Object),并支持 T4 模板生成的类等。此外,由于 PetaPoco 是开源项目,因此添加条件来处理如空间数据等特定情况会很容易。
  2. Dapper :
    性能方面高于 PetaPoco,速度只稍逊于手工编码的数据访问层(DAL、Data AccessLayer),还有一些对初学者会有帮助的、位于官网上的系列博文和一些独立博文教程。

一、DataFirst

使用EF,创建Model (DataFirst)

  1. 在解决方案中,创建所使用的项目。例如:ASP网站项目、WinForm窗体项目……

  2. 在新建的项目中,添加 > 新建项 > 数据 > ADO.NET实体数据模型(.edmx)

  3. 在新建的.edmx中选择:从数据库生成(DatabaseFirst)

  4. 选择新建连接,填写连接数据库 > 填写登录数据库信息 > 选择或输入需要生成模型的数据库名称.

  5. 选择需要创建模型的表、视图、存储过程

  6. 点击下一步,直接生成(有些电脑可能会出现警告信息,忽视即可)。

    image

    ​ (图片不清晰的话请在:浏览器上,鼠标右键,在新标签页中打开图片。即可看到清晰图片)

使用EF创建的内容

创建完成后,项目会默认引入两个命名空间:EntityFramework、System.Data.Entity

  1. 最外层为.edmx文件,EF模型设计器,展示从数据库创建的模型,包括:表名、属性(表字段)、导航属性(外键、表与表之间的对应关系)、表与表之间1对1、1对多的关系。

  2. Model1.Context.ttModel.Designer.csModel.edmx.diagramModel1.tt

    1. Model1.Context.tt:包含Model1.Context.csModel1.Context.tt由T4模板生成,不可更改,Model1.Context.cs继承自DbContext,包括构造方法(继承自父类的构造方法,并在此传递配置文件的数据库连接字符串)、各个表所对应的Dbset<T>类型的自动属性。
    2. Model.Designer.cs:自动生成,无需更改
    3. Model.edmx.diagram:描述edmx文件的元素信息(名称、位置……)
    4. Model1.tt:包含生成的Model.cs,自身由T4模板生成,无需修改

使用EF进行增删改查

查询

1
2
3
4
5
6
7
8
9
StudentMISEntities stu = new StudentMISEntities();
//IQueryable<Users> userInfoList=from u in stu.Users
var userInfoList = from u in stu.Users
where u.id == 10
select u;
foreach (var item in userInfoList)//EF延迟加载机制,数据用到的时候才去数据库中查询,不用的时候不去查询,提高程序性能
{
Response.Write(item.uName);
}

增加

1
2
3
4
5
6
7
Users userInfo = new Users();
userInfo.uName = "2333";
userInfo.uPassword = "123";
userInfo.uRole = "学生";
StudentMISEntities stu = new StudentMISEntities();//创建EF数据操作类实例
stu.Users.Add(userInfo);//把数据添加到EF,并添加标记
stu.SaveChanges();//把数据保存到数据库

修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
StudentMISEntities stu = new StudentMISEntities();
var userInfoList = from u in stu.Users
where u.id == 16
select u;
Users userInfo = userInfoList.FirstOrDefault();//返回第一个元素,如果没有的话,返回null
if (userInfo!=null)
{
userInfo.uPassword = "12345678";
stu.Entry<Users>(userInfo).State = System.Data.Entity.EntityState.Modified;//添加修改标记
stu.SaveChanges();
Response.Write(userInfo.uPassword);
}
else
{
Response.Write("要修改的数据不存在");
}

删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 StudentMISEntities stu = new StudentMISEntities();
var userInfoList = from u in stu.Users
where u.id == 17
select u;
Users userInfo = userInfoList.FirstOrDefault();//返回第一个元素,如果没有的话,返回null
if (userInfo!=null)
{
//stu.Users.Remove(userInfo);
stu.Entry<Users>(userInfo).State = System.Data.Entity.EntityState.Deleted;//添加删除标记
stu.SaveChanges();
Response.Write("已删除");
}
else
{
Response.Write("要删除的数据不存在");
}

二、ModelFirst

除了支持 DatabaseFirst 外,EF 还支持 ModelFirst 、 CodeFirst,这两种类型不同于DatabaseFirst 先建立好数据库而后映射 Model , ModelFirst 是通过 Model 映射创建数据库,而 CodeFirst 类似于 ModelFirst, 通过代码逻辑自行创建 Model 并映射数据库,但是不同于 ModelFirst 由 VS 自动创建。

Model先行,通过Model创建数据库

步骤:

  1. 在新建的项目中,添加 > 新建项 > 数据 > ADO.NET实体数据模型(.edmx)

  2. 在新建的.edmx中选择:空模型(ModelFirst)

  3. 在.edmx图形化窗口中,右键 > 新增 > 实体

  4. 在新增实体页面 填写: 实体名(表名)、是否创建键属性(是否创建主键)、属性名称、属性类型 > 确定

  5. 在.edmx图形化窗口中,在 实体 上右键 > 新增 > 标量属性(表字段)

  6. 选中标量属性,按快捷键 F4 或调出属性面板,可以更改标量属性的:类型、名称、长度(必改

  7. 在多个实体,且实体之间有关联的时候,在.edmx图形化设计页面中,右键 > 新建 > 关联

  8. 在添加关联页面设置内容(默认即可)

  9. 在所有模型创建完毕,在.edmx页面 右键 > 根据模型生成数据库

  10. 在 “生成数据库 向导” 页面,新建数据库连接 > 设置连接字符串 > 选择需要创建表的数据库 > 点击下一步

  11. 生成数据库向导中,会生成 DDL(数据库设计语言),并另存为 .sql 文件

  12. 生成 .sql 文件后,建立的Model并没有映射到数据库引擎中,需要在 .sql 文件中,右键 > 执行,即可。
    image

    ​ (图片不清晰的话请在:浏览器上,鼠标右键,在新标签页中打开图片。即可看到清晰图片)

注意事项:

  1. 新建 标量属性 后,需要在属性面板对 标量属性的 长度数据类型 进行限制,string 长度即为数据库内字段的 NVarChar 的长度 。
  2. 在 .edmx 图形化页面 新建完实体 后,解决方案里面可能没有生成 Model.tt 文件(包含 Model 模型.cs),这时候在 .edmx 图形化页面: 右键 > 添加代码生成项 > 数据 >EF 版本.x DbContext 生成器 > 确定
  3. 关于实体更新
    1. 需映射数据库更新
      1. 更新实体后,VS 在生成的DDL数据库脚本中,是把原先创建好的表全部删除,再重新创建更新后的实体所对应的表,如果原数据表中存在数据,数据也会一并删除。(2). 在更新数据库时一定要谨慎,做好数据库备份,同时如果只是小的改动(扩大字符长度),手动在数据库里 修改即可,或者仅把更新的sql脚本部分拷贝出来,复制到SQL Server执行

三、CodeFirst

先写代码,通过代码创建实体,映射数据库

步骤:

  1. 新建项目,在项目里面通过 NuGet程序包 安装EntityFramework(该操作会自动引入EntityFramework以及相对应的命名空间)

  2. 在配置文件里面添加连接字符串

    1
    2
    3
    <connectionStrings>
    <add name="CodeFirstDemo" connectionString="server=.;uid=用户;pwd=密码;database=Test2" providerName="System.Data.SqlClient"/>
    </connectionStrings>
  3. 创建类 .cs ,设置类的属性、类之间的关联

studentInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace CodeFirstDemo
{
public class StudentInfo
{
public int Id { get; set; }
public string stuName { get; set; }
public DateTime subTime { get; set; }

//建立联系
public virtual ClassInfo classInfo { get; set; }
}
}

classInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace CodeFirstDemo
{
public class ClassInfo
{
//Id添加主键标识
[Key]
public int Id { get; set; }

//数据类型为NVarChar(32),且不可为空
[StringLength(32)]
[Required]
public string clsName { get; set; }

[Required]
public DateTime sbuTime { get; set; }

//建立联系
public virtual ICollection<StudentInfo> sutdentInfo { get; set; }
}
}
  1. 创建 EF数据操作类 codeFirstDbcontex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace CodeFirst
{
public class codeFirstDbcontext:DbContext
{
//调用父类的构造方法,并传递 配置文件内的 数据库连接字符串 字段
public codeFirstDbcontext() : base("name=connStr") { }

public DbSet<ClassInfo> ClassInfo { get; set; }
public DbSet<StudentInfo> Student { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//实体模型映射成表的时候执行

//移除 将表名设置为实体类型名称的复数版本的约定
//模型名映射在数据库表的时候,可能会出现复数(s)
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
  1. 执行
1
2
3
4
5
6
7
8
codeFirstDbcontext db = new codeFirstDbcontext();
//如果 数据库中不存在库,则创建
db.Database.CreateIfNotExists();
ClassInfo clsInfo = new ClassInfo();
clsInfo.clsName = "16级.NET班";
clsInfo.sbuTime = DateTime.Now;
db.ClassInfo.Add(clsInfo);
db.SaveChanges();
image
image

​ (图片不清晰的话请在:浏览器上,鼠标右键,在新标签页中打开图片。即可看到清晰图片)

四、Linq查询和Lambda查询

Linq查询

Linq查询全部列并降序

1
2
3
4
5
6
7
8
var userInfoList = from u in db.Users
where u.id > 8
orderby u.id ascending //descending降序
select u;
foreach (Users userInfo in userInfoList)
{
Response.Write(userInfo.id + ":" + userInfo.uName + "<hr/>");
}

Linq分页

1
2
3
4
5
6
7
8
9
10
int pageIndex = 3;
int pageSize = 3;
var userInfoList = (from u in db.Users
where u.id > 8
orderby u.id ascending //descending降序
select u).Skip((pageIndex - 1) * pageSize).Take(pageSize);
foreach (Users userInfo in userInfoList)
{
Response.Write(userInfo.id + ":" + userInfo.uName + "<hr/>");
}

Linq查询部分列

1
2
3
4
5
6
7
8
var userInfoList = from u in db.Users
where u.id > 8
//使用匿名类,设置两个字段UName,UPwd
select new { UName = u.uName, UPwd = u.uPassword };
foreach (var userInfo in userInfoList)
{
Response.Write(userInfo.UName+":"+userInfo.UPwd+"<hr/>");
}

Lambda查询

过滤筛选

1
2
3
4
5
var userInfoList = db.Users.Where<Users>(u => u.id > 8);
foreach (var userInfo in userInfoList)
{
Response.Write(userInfo.id + ":" + userInfo.uName + "<hr/>");
}

Lambda升序排序排序查询

对Id进行升序排序排序

1
var userInfoList = db.Users.Where<Users>(U => true).OrderBy<Users, int>(u => u.id);

对Id进行降序排序排序

1
2
3
4
5
var userInfoList = db.Users.Where<Users>(U => true).OrderByDescending<Users, int>(u => u.id);
foreach (var userInfo in userInfoList)
{
Response.Write(userInfo.id + ":" + userInfo.uName + "<hr/>");
}

在以用户名为前提倒序排序的基础上,如果用户名相同,则使用Id作为依据对用户名相同的用户进行倒序排序

1
var userInfoList = db.Users.Where<Users>(u => true).OrderByDescending(u => u.uName).ThenByDescending<Users,int>(u => u.id);

Lambda分页查询

1
2
3
4
5
6
7
int pageIndex = 3;
int pageSize = 2;
var userInfoList = db.Users.Where<Users>(U => true).OrderByDescending<Users, int>(u => u.id).Skip<Users>((pageIndex-1)*pageSize).Take<Users>(pageSize);
foreach (var userInfo in userInfoList)
{
Response.Write(userInfo.id + ":" + userInfo.uName + "<hr/>");
}